use crate::dto;
use crate::dto::auth::auth::AuthInfo;
use crate::service::Error;
use redis::AsyncCommands;
use serde::{Deserialize, Serialize};
use wanmesh_common::dto::ResponseCode;
use wanmesh_common::error::ResponseError;

const ACCESS_TOKEN_EXPIRES_IN: u64 = 30 * 60;
const REFRESH_TOKEN_EXPIRES_IN: u64 = ACCESS_TOKEN_EXPIRES_IN * 2;

#[derive(Serialize, Deserialize, Debug, Clone)]
struct RefreshTokenInfo {
    access_token: String,
    auth: AuthInfo,
}

#[async_trait::async_trait]
pub trait AuthTokenService: Sync + Send {
    async fn create_token(&self, info: &AuthInfo) -> Result<dto::auth::Token, Error>;

    async fn get_auth_info(&self, access_token: &str) -> Result<AuthInfo, Error>;
    async fn refresh_token(&self, refresh_token: &str) -> Result<dto::auth::Token, Error>;
}

pub struct DefaultAuthTokenService {
    redis_conn: redis::aio::MultiplexedConnection,
}

impl DefaultAuthTokenService {
    pub fn new(redis_conn: redis::aio::MultiplexedConnection) -> Self {
        DefaultAuthTokenService { redis_conn }
    }

    async fn store_access_token(&self, access_token: &str, info: &AuthInfo) -> Result<(), Error> {
        let info = serde_json::to_string(info).map_err(anyhow::Error::new)?;

        let mut conn = self.redis_conn.clone();
        let _: () = conn
            .set_ex(
                key_access_token(access_token),
                info.clone(),
                ACCESS_TOKEN_EXPIRES_IN,
            )
            .await
            .map_err(anyhow::Error::new)?;

        Ok(())
    }

    async fn store_refresh_token(
        &self,
        refresh_token: &str,
        info: &RefreshTokenInfo,
    ) -> Result<(), Error> {
        let info = serde_json::to_string(info).map_err(anyhow::Error::new)?;

        let mut conn = self.redis_conn.clone();
        let _: () = conn
            .set_ex(
                key_refresh_token(refresh_token),
                info.clone(),
                REFRESH_TOKEN_EXPIRES_IN,
            )
            .await
            .map_err(anyhow::Error::new)?;

        Ok(())
    }

    async fn get_refresh_token_info(&self, refresh_token: &str) -> Result<RefreshTokenInfo, Error> {
        let mut conn = self.redis_conn.clone();
        let info: Option<String> = conn
            .get(key_refresh_token(refresh_token))
            .await
            .map_err(anyhow::Error::new)?;

        let info: RefreshTokenInfo = match info {
            Some(info) => serde_json::from_str(info.as_str()).map_err(anyhow::Error::new)?,
            None => {
                return Err(Error::ResponseError(ResponseError {
                    code: ResponseCode::UNAUTHORIZED,
                    message: "无效的refresh_token".to_string(),
                    backtrace: Default::default(),
                }));
            }
        };

        Ok(info)
    }

    async fn delete_access_token(&self, access_token: &str) -> Result<(), Error> {
        let mut conn = self.redis_conn.clone();
        let _: () = conn
            .del(key_access_token(access_token))
            .await
            .map_err(anyhow::Error::new)?;

        Ok(())
    }

    async fn delete_refresh_token(&self, refresh_token: &str) -> Result<(), Error> {
        let mut conn = self.redis_conn.clone();
        let _: () = conn
            .del(key_refresh_token(refresh_token))
            .await
            .map_err(anyhow::Error::new)?;

        Ok(())
    }
}

#[async_trait::async_trait]
impl AuthTokenService for DefaultAuthTokenService {
    async fn create_token(&self, info: &AuthInfo) -> Result<dto::auth::Token, Error> {
        let access_token = wanmesh_common::util::new_id()?;
        let refresh_token = wanmesh_common::util::new_id()?;

        self.store_access_token(access_token.as_str(), info).await?;
        self.store_refresh_token(
            refresh_token.as_str(),
            &RefreshTokenInfo {
                access_token: access_token.clone(),
                auth: info.clone(),
            },
        )
        .await?;

        Ok(dto::auth::Token {
            access_token,
            refresh_token,
            expires_in: ACCESS_TOKEN_EXPIRES_IN,
            refresh_expires_in: REFRESH_TOKEN_EXPIRES_IN,
        })
    }

    async fn get_auth_info(&self, access_token: &str) -> Result<AuthInfo, Error> {
        let mut conn = self.redis_conn.clone();
        let info: Option<String> = conn
            .get(key_access_token(access_token))
            .await
            .map_err(anyhow::Error::new)?;

        let info: AuthInfo = match info {
            Some(info) => serde_json::from_str(info.as_str()).map_err(anyhow::Error::new)?,
            None => {
                return Err(Error::ResponseError(ResponseError {
                    code: ResponseCode::UNAUTHORIZED,
                    message: "无效的access_token".to_string(),
                    backtrace: Default::default(),
                }));
            }
        };

        Ok(info)
    }

    async fn refresh_token(&self, refresh_token: &str) -> Result<dto::auth::Token, Error> {
        let info = self.get_refresh_token_info(refresh_token).await?;
        self.delete_refresh_token(refresh_token).await?;
        self.delete_access_token(info.access_token.as_str()).await?;
        self.create_token(&info.auth).await
    }
}

fn key_access_token(access_token: &str) -> String {
    format!("wanmesh:server:auth:access_token:{}", access_token)
}

fn key_refresh_token(refresh_token: &str) -> String {
    format!("wanmesh:server:auth:refresh_token:{}", refresh_token)
}
